﻿using System.Collections.Generic;
using P4 = Perforce.P4;

namespace Red.Core.Perforce
{
    public enum StreamType
    {
        Development = P4.StreamType.Development,
        Mainline = P4.StreamType.Mainline,
        Release = P4.StreamType.Release,
        Virtual = P4.StreamType.Virtual,
        Task = P4.StreamType.Task
    }

    public class StreamBuilder
    {
        #region Public Types

        public class Path
        {
            public string Left { get; set; }
            public string Right { get; set; }
        }

        /// <summary>
        /// Represent the various path types 
        /// </summary>
        /// <remarks>
        /// Check out <a href="https://www.perforce.com/perforce/doc.current/manuals/p4v/streams_views.html">the perforce documentation on stream views</a> for more information
        /// </remarks>
        public class PathContainer
        {
            public List<Path> Share { get; private set; } = new List<Path>();
            public List<Path> Exclude { get; private set; } = new List<Path>();

            public bool HasPaths => Share.Count + Exclude.Count > 0;
        }

        #endregion Public Types

        #region Public Properties

        public string Id { get; set; }
        public string Name { get; set; }
        public string Parent { get; set; }
        public StreamType? Type { get; set; }
        public string Owner { get; set; }

        //Options
        public bool LockSpecificationToOwner { get; set; }
        public bool LockContentsToOwner { get; set; }

        public PathContainer Paths { get; private set; } = new PathContainer();

        public string FullyQualifiedId => ResolveId();

        #endregion Public Properties

        #region Public Methods

        public bool AddPathSpecification( string specification )
        {
            string[] mappings = specification.Split( '\n' );

            foreach( string map in mappings )
            {
                string[] components = map.Split( ' ' );

                if ( components.Length < 2 )
                    return false;

                if ( components.Length > 3 )
                    return false;

                Path path = new Path();
                path.Left = components[ 1 ].Trim();

                if ( components.Length == 3 )
                    path.Right = components[ 2 ].Trim();

                if ( components[ 0 ] == "share" )
                {
                    Paths.Share.Add( path );
                }
                else if ( components[ 0 ] == "exclude" )
                {
                    Paths.Exclude.Add( path );
                }
                else
                {
                    return false;
                }
            }

            return true;
        }

        #endregion Public Methods

        #region Internal Methods

        /// <summary>
        /// Builds a perforce stream object based on the information specified in this builder object
        /// </summary>
        /// <remarks>
        /// Based on the example found in the <a href="https://swarm.workshop.perforce.com/projects/perforce-software-p4api-net/files/main/p4api.net/Repository.Stream.cs">public p4api.net swarm repository</a>
        /// </remarks>
        /// <returns>null if there was a problem, otherwise a ready to use Perforce stream object</returns>
        internal P4.Stream Construct()
        {
            if ( Id == null )
                return null;

            if ( Name == null )
                return null;

            if ( Type == null )
                return null;

            if ( Owner == null )
                return null;

            if ( FullyQualifiedId == null )
                return null;

            P4.Stream stream = new P4.Stream();

            stream.Id = FullyQualifiedId;
            stream.Type = (P4.StreamType)Type;
            stream.Name = Name;
            stream.Parent = new P4.DepotPath( Parent );
            stream.OwnerName = Owner;

            if ( LockSpecificationToOwner )
                stream.Options |= P4.StreamOption.Locked;

            if ( LockContentsToOwner )
                stream.Options |= P4.StreamOption.OwnerSubmit;

            if( Type == StreamType.Virtual )
            {
                stream.Options |= P4.StreamOption.NoToParent;
                stream.Options |= P4.StreamOption.NoFromParent;
            }

            if( Paths.HasPaths )
            {
                stream.Paths = new P4.ViewMap();

                AddPaths( stream, P4.MapType.Share, Paths.Share );
                AddPaths( stream, P4.MapType.StreamPathExclude, Paths.Exclude );
            }

            return stream;
        }

        #endregion Internal Methods

        #region Private Methods

        private static bool ResolveId( ref string id, string parent )
        {
            if ( parent == null )
                return false;

            int rootEnd = parent.IndexOf( '/', 2 );
            string root = parent.Substring( 0, rootEnd + 1 );

            if ( id.StartsWith( root ) )
                return true;

            id = root + id;

            return true;
        }

        private string ResolveId()
        {
            string id = Id;

            if ( ResolveId( ref id, Parent ) )
                return id;

            return null;
        }

        private void AddPaths( P4.Stream stream, P4.MapType type, IEnumerable<Path> paths )
        {
            foreach ( Path path in paths )
            {
                P4.DepotPath right = path.Right == null ? null : new P4.DepotPath( path.Right );
                P4.MapEntry entry = new P4.MapEntry( type, new P4.DepotPath( path.Left ), right );
                stream.Paths.Add( entry );
            }
        }

        #endregion Private Methods
    }
}
